package polyu.comp;

import java.io.*;
import java.text.*;
import java.util.*;

import android.content.*;
import android.net.*;

/**
 * 
 * This class is in memory data management class.
 * It retrieves data from network and local files
 * It also management the data caching and other data mgmt stuffs.
* This class manage Network operation.
* It detect the network connection status.
* If offline, get from cache. If online, get from network
*/
/*
   #1 request all users in DB
   REQ: friend all
   RES: ok tom,jerry
   
   #2 get user's following list
   REQ: friend follow jason
   RES: ok tom,jerry
   
   #3 update following list for user
   REQ: friend update jason tom,jerry,ben
   RES: ok
   
   #4 get who is following user
   REQ: friend follower jason
   RES: ok tom,jerry
   

   #5 synchronize the activity with server
   REQ: activity sync jason 30
   RES: ok tom,1333174320,1333177937,1332745920,874 jerry,1333174320,1333177937,1332745920,874
   
   #6 add an activity to server
   REQ: activity add uid,epoch_beg,epoch_end,length,trace
   EXA: activity add jason,1333178959,1333178959,123,null
   RES: ok
*/
public class DataManager {
	
	// local storage file name
	public static String f_act 	= "activity_file";
	public static String f_user = "user_file";
	
	// The local network command stored here
	public static String f_localcmd = "local_cmdq";
	
	
	// private data
	private Vector<ActionItem> 	mItems;
	private Vector<String>		mUsers;
	private Vector<String>		mFollow;
	private Vector<String>		mFollower;
	
	private Context				mCx;
	
	/**
	 * Singleton Model
	 */
	private static DataManager mInst = null;
	private DataManager() {
	}
	
	public static DataManager instance() {
		if (mInst == null)
			mInst = new DataManager();
		return mInst;
	}
	
	public void setContext(Context cx) {
		this.mCx = cx;
	}
	
	public Date getLastSyncDate() {
		return new Date(this.getLastSyncLong() * 1000);
	}
	
	public long getLastSyncLong() {
		String fileName = mCx.getResources().getString(R.string.app_name);
		SharedPreferences settings = mCx.getSharedPreferences(fileName, 0);
		
		return settings.getInt("lastsync", 0);
	}
	private void setLastSync() {
		String fileName = mCx.getResources().getString(R.string.app_name);
		SharedPreferences settings = mCx.getSharedPreferences(fileName, 0);
		SharedPreferences.Editor editor = settings.edit();
		int t = (int) (new Date().getTime() / 1000);
		editor.putInt("lastsync", t);
		
		editor.apply();
	}
	
	private void setUsers(String arg) {
		this.mUsers = new Vector<String>();
		Collections.addAll(this.mUsers, arg.split(","));
	}
	
	private void setFollow(String arg) {
		this.mFollow = new Vector<String>();
		Collections.addAll(this.mFollow, arg.split(","));
	}
	
	private void setFollower(String arg) {
		this.mFollower = new Vector<String>();
		Collections.addAll(this.mFollower, arg.split(","));
	}
	
	private String vectorToString(Vector<String> arg) {
		
		if (arg.size() == 0)
			return "";
		
		StringBuilder sb = new StringBuilder();
		for(String s : arg)
		{
			sb.append(s);
			sb.append(',');
		}
		
		sb.deleteCharAt(sb.length()-1);
		
		return sb.toString();
	}
	
	
	private void saveUserCache() {

		try {
			FileOutputStream fos = mCx.openFileOutput(DataManager.f_user, Context.MODE_PRIVATE);
			PrintWriter out = new PrintWriter(fos, true);
			
			out.println(vectorToString(this.mUsers));
			out.println(vectorToString(this.mFollow));
			out.println(vectorToString(this.mFollower));
			
			out.close();
			fos.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// User getter
	public String[] getUsers() {

		if (this.isConnected()) {
			// Connected, retrieve from internet.
			String retall = Util.netRequest("friend all").substring(3);
			// update Local
			this.setUsers(retall);
			// update cache
			this.saveUserCache();	
		}
		return this.mUsers.toArray(new String[0]);
	}

	public String[] getFollow() {
		if (this.isConnected()) {
			// Connected, retrieve from internet.
			String retall = Util.netRequest(String.format("friend follow %s",
					Util.uid));
			retall = retall.substring(3);
			// update Local
			this.setFollow(retall);
			// update cache
			this.saveUserCache();
		}

		return this.mFollow.toArray(new String[0]);
	}

	public String[] getFollower() {
		if (this.isConnected()) {
			// Connected, retrieve from internet.
			String retall = Util.netRequest(String.format("friend follower %s",
					Util.uid));
			retall = retall.substring(3);
			// update Local
			this.setFollower(retall);
			// update cache
			this.saveUserCache();
		}

		return this.mFollower.toArray(new String[0]);
	}
	
	// Return true if updated to network
	// Return false if stored locally
	public boolean setFollow(Vector<String> args) {
		
		// 1. local memory modification
		this.mFollow = args;
		
		// 2. server operation or queue operation
		String cmd = String.format("friend update %s %s",
				Util.uid, this.vectorToString(this.mFollow));
		
		if (this.isConnected()) {
			Util.netRequest(cmd);
			return true;
		} else {
			this.saveQueue(cmd);
			return false;
		}
	}
	
	// put an item to queue
	public void saveQueue(String cmd) {
		try {
    		// write activity
			FileOutputStream fos = mCx.openFileOutput(DataManager.f_localcmd, Context.MODE_APPEND);
			PrintWriter out = new PrintWriter(fos, true);
			
			out.println(cmd);
			
			out.close();
			fos.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	// flush all items in queue
	// and delete the file
	public void flushQueue() {
		
		// Must be connected
		if (! this.isConnected())
			return ;
		
		
		try {
    		// read command
			FileInputStream fis = mCx.openFileInput(DataManager.f_localcmd);
			BufferedReader in= new BufferedReader(new InputStreamReader(fis));
			String aline = null;

			while((aline = in.readLine())!=null){
				String cmd = aline.trim();
				if (cmd.length() > 3)
					Util.netRequest(cmd);
			}

			in.close();
			fis.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		mCx.deleteFile(this.f_localcmd);
		
	}
	
	public Vector<ActionItem> getItems() {
		return this.mItems;
	}
	
	public boolean addActivity(ActionItem ai) {
		
		String cmd = "activity add " + ai.toUpdateProtocolString();
		
		// check online
		if (! this.isConnected()) {
			this.saveQueue(cmd);
			return false;
		}
		
		Util.netRequest(cmd);
		
		return true;
		
	}
	
	public boolean pullFromServer() {
		
		if (! this.isConnected())
			return false;
		
		// create a whole new list
		this.mItems =  new Vector<ActionItem>();
		
		String cmd = String.format("activity sync %s %s",
				Util.uid, Util.cachep);
		
		//cmd = String.format("test");
		
		String retall = Util.netRequest(cmd);
		
		if (retall.length() > 3) {
			String []items = retall.substring(3).split(" ");
			
			for (String s : items) {
				ActionItem ai = new ActionItem(s);
				this.mItems.add(ai);
			}
		}
		
		this.setLastSync();
		this.writeToLocalFile();
		
		return true;
	}
	
	// Write Memory object to local file
	public void writeToLocalFile() {
		
		try {
    		// write activity
			FileOutputStream fos = mCx.openFileOutput(DataManager.f_act, Context.MODE_PRIVATE);
			PrintWriter out = new PrintWriter(fos, true);
			
			for (int i = 0; i < this.mItems.size(); i++) {
				String s = this.mItems.get(i).toProtocolString();
				out.println(s);
			}
			
			out.close();
			fos.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
	
	public void initFromLocalFile() {
		// create a whole new list
		this.mItems =  new Vector<ActionItem>();

    	try {
    		// read activity
			FileInputStream fis = mCx.openFileInput(DataManager.f_act);
			BufferedReader in= new BufferedReader(new InputStreamReader(fis));
			String aline = null;

			while((aline = in.readLine())!=null){
				ActionItem ai = new ActionItem(aline);
				this.mItems.add(ai);
			}

			in.close();
			fis.close();
			
			// read user info
			fis = mCx.openFileInput(DataManager.f_user);
			in= new BufferedReader(new InputStreamReader(fis));
			String all = in.readLine();
			String follow = in.readLine();
			String follower = in.readLine();
			this.setUsers(all);
			this.setFollow(follow);
			this.setFollower(follower);
			
			in.close();
			fis.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
    	
	}
	
	/**
	 * Whether connected to Internet
	 */
	public boolean isConnected() {
		ConnectivityManager cManager = (ConnectivityManager) mCx
				.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo info = cManager.getActiveNetworkInfo();
		//if (info != null && info.isAvailable() && info.getType() == ConnectivityManager.TYPE_WIFI) {
		if (info != null && info.isAvailable()) {
			return true;
		} else {
			return false;
		}
	}

}
